#! -*- coding:utf-8 -*-

import logging
import time
from datetime import datetime, timedelta

from requests.exceptions import RequestException

from common import orm
from common.platform.common.db import reverse_not_existed_fronzen, unfreeze_balance, delete_withdraw_trans_log
from common.platform.common.model import M_TRANSACTION_TYPE, ThirdTransaction, ThirdWithdrawTransactionLog
from common.platform.integrate import get_third_handler
from common.utils.decorator import retry
from common.utils.thread import Thread

_LOGGER = logging.getLogger(__name__)

BATCH_COUNT = 200
IDLE_SLEEP_TIME = 120
CHECK_TIME_MINUTES_BEFORE_NOW = 5
MAX_TRANS_ID = None


class ThirdTransactionCheck(Thread):
    MAX_TRANS_ID = None

    def run(self):
        _LOGGER.info('starting auto check third transaction ...')
        self.MAX_TRANS_ID = self.get_max_transaction_id()
        continuous_fail_count = 0
        while not self.shutdown_flag.is_set():
            try:
                has_more = self.check_deposit_batch()
                has_more = self.check_withdraw_batch() or has_more
                if not has_more:
                    time.sleep(IDLE_SLEEP_TIME)
                continuous_fail_count = max(0, continuous_fail_count - 1)
            except:
                continuous_fail_count += 1
                if continuous_fail_count > 10:
                    _LOGGER.exception('auto check third transaction fail: max_trans_id: [%s]' % self.MAX_TRANS_ID)
                    break
            finally:
                orm.session.close()

    def get_max_transaction_id(self):
        last_checked_trans = ThirdTransaction.query.filter(
            (ThirdTransaction.checked == True) & (ThirdTransaction.created_at <= (datetime.utcnow() - timedelta(
                minutes=CHECK_TIME_MINUTES_BEFORE_NOW)))).order_by(
            ThirdTransaction.id.desc()).first()
        if last_checked_trans:
            return last_checked_trans.id

    def check_deposit_batch(self):
        _LOGGER.info('start batch check, max trans id : %s' % self.MAX_TRANS_ID)
        query = ThirdTransaction.query.filter(
            (ThirdTransaction.checked == False) & (ThirdTransaction.created_at <= (datetime.utcnow() - timedelta(
                minutes=CHECK_TIME_MINUTES_BEFORE_NOW))))
        if self.MAX_TRANS_ID:
            query = query.filter(ThirdTransaction.id >= self.MAX_TRANS_ID)
        query = query.order_by(ThirdTransaction.id.asc()).limit(BATCH_COUNT)
        has_more = False
        for trans in query.all():
            if trans.checked:
                continue
            self.check_deposit(trans)
            self.MAX_TRANS_ID = trans.id
            has_more = True
        return has_more

    @retry(RequestException)
    def check_deposit(self, trans):
        if trans.type in [M_TRANSACTION_TYPE.FROZEN]:
            if not get_third_handler(trans.platform).check_trans_existed(trans.ref_id):
                _LOGGER.info('begin auto reverse third frozen_balance: %s' % trans.ref_id)
                reverse_status, reverse_trans = reverse_not_existed_fronzen(trans.user_id, trans.ref_id)
                if reverse_status:
                    _LOGGER.error('success finish auto reverse frozen_balance: %s' % trans.ref_id)

        trans.checked = True
        trans.save()

    def check_withdraw_batch(self):
        withdraw_trans_logs = ThirdWithdrawTransactionLog.query.filter(ThirdWithdrawTransactionLog.created_at <= (
                datetime.utcnow() - timedelta(minutes=CHECK_TIME_MINUTES_BEFORE_NOW))).limit(BATCH_COUNT).all()
        has_more = False
        for trans in withdraw_trans_logs:
            self.check_withdraw(trans)
            has_more = True
        return has_more

    @retry(RequestException)
    def check_withdraw(self, trans):
        if get_third_handler(trans.platform).check_trans_existed(trans.ref_id):
            _LOGGER.info('begin auto unfreeze_balance third: %s' % trans.ref_id)
            unfreeze_balance(trans.user_id, trans.platform, trans.amount, trans.ref_id)
            _LOGGER.error('success auto unfreeze_balance metis: %s' % trans.ref_id)
        delete_withdraw_trans_log(trans.id)
